HDU 5794 A Simple Chess(dp、容斥、Lucas)

题意:

$给定N\times M的棋盘,N,M\le 10^{18},棋盘上有R\le 100个障碍物$
$现有一个马从(1, 1)到(N, M),只能向右和下走,问方法数$

分析:

$考虑到达(x, y)没有障碍物的方法数,其实假设右跳a次,下跳b次$
$即2a+b=x, a+2b=y, \Rightarrow a + b = { x + y \over 3 }$
$即a=x - { x + y \over 3 }, b = y - { x + y \over 3 }$
$显然方法数是C_{a+b}^a$
$不过泥打表找规律发现杨辉三角,找到有解的直线方程转化也是兹磁的,(窝萌是这么干的)$
$之后就是容斥了,把经过障碍物的减掉$
$首先对障碍物排序,然后dp一波根据偏序关系来$
$令全集ans=calc(n, m)$
$f[i]:=到达i,且以i为第一个障碍物的方法数$
$最终答案必然是ans=ans-\sum f[i]\times calc(n-obstacle[i].x+1, m-obstacle[i].y+1)$
$显然每个f[i]是互斥事件,因为这些路径是不重复的,注意那个第一个(这个是统计套路。。$
$并且所有经过障碍物的路径必然被其中一个f[i]包含$
$所以所有的f[i]构成经过障碍物的全集$
$f[i]=calc(obstacle[i].x, obstacle[i].y)$
$-\sum f[j]\times calc(obstacle[i].x-obstacle[j].x+1, obstacle[i].y-obstacle[j].y+1)$
$obstacle[j]偏序小于obstacle[i]$
$注意判断不可达别用0,因为会模出来0,$→_→
$时间复杂度O(P+r^2 log P)$

代码:

//
//  Created by TaoSama on 2016-08-04
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 110119;

typedef long long LL;
LL n, m, ox[N], oy[N];
int r;

LL g[1005][1005];
void DP() {
    memset(g, 0, sizeof(g));
    g[1][1] = 1;
    for(int j = 1; j <= n; j++) {
        for(int k = 1; k <= m; k++) {
            int ok = 1;
            for(int i = 1; i <= r; i++) {
                if(j == ox[i] && k == oy[i]) {
                    ok = 0;
                    break;
                }
            }
            //if(!ok) printf("NO\n");
            if(!ok) {
                g[j][k] = 0;
                continue;
            }
            g[j + 1][k + 2] += g[j][k];
            g[j + 2][k + 1] += g[j][k];
            g[j + 1][k + 2] %= MOD;
            g[j + 2][k + 1] %= MOD;
        }
    }
}

LL quick(LL x, LL n) {
    LL ret = 1;
    for(; n; n >>= 1) {
        if(n & 1) ret = ret * x % MOD;
        x = x * x % MOD;
    }
    return ret;
}

LL fact[N], invf[N];
void gao() {
    fact[0] = invf[0] = 1;
    for(int i = 1; i <= MOD; ++i) {
        fact[i] = fact[i - 1] * i % MOD;
        invf[i] = quick(fact[i], MOD - 2);
    }
}

LL C(int n, int m) {
    if(n < m) return 0;
    return fact[n] * invf[m] % MOD * invf[n - m] % MOD;
}

LL lucas(LL n, LL m) {
    if(m == 0) return 1;
    return C(n % MOD, m % MOD) * lucas(n / MOD, m / MOD) % MOD;
}

LL calc(LL x, LL y) {
    LL n = x + y + 1;
    if(n % 3) return -1;
    n /= 3;
    if(x < n || y < n) return -1;
    LL m = x - n;
    n--;
    return lucas(n, m);
}

int id[N];
LL f[N]; //to i, and let i be the first obstacle

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt", "w", stdout);
#endif
    ios_base::sync_with_stdio(0);

    gao();
    while(scanf("%I64d%I64d%d", &n, &m, &r) == 3) {
        for(int i = 1; i <= r; ++i) scanf("%I64d%I64d", ox + i, oy + i);

        for(int i = 1; i <= r; ++i) id[i] = i;
        sort(id + 1, id + 1 + r, [&](int x, int y) {
            return make_pair(ox[x], oy[x]) < make_pair(ox[y], oy[y]);
        });

        LL ans = calc(n, m);
        if(~ans) {
            for(int i = 1; i <= r; ++i) {
                int u = id[i];
                LL& cur = f[i];
                cur = calc(ox[u], oy[u]);
                if(cur == -1) continue;
                LL to = calc(n - ox[u] + 1, m - oy[u] + 1);
                if(to == -1) continue;
                for(int j = 1; j < i; ++j) {
                    if(f[j] == -1) continue;
                    int v = id[j];
                    if(ox[u] > ox[v] && oy[u] > oy[v]) {
                        LL tmp = calc(ox[u] - ox[v] + 1, oy[u] - oy[v] + 1);
                        if(tmp == -1) continue;
                        cur -= f[j] * tmp % MOD;
                        cur %= MOD;
                    }
                }
                ans = (ans - cur * to % MOD) % MOD;
            }
        } else ans = 0;
        ans = (ans + MOD) % MOD;
//        DP();
//        if(g[n][m] != ans) {
//            puts("WA");
//            pr(g[n][m]); prln(ans);
//        }
        static int kase = 0;
        printf("Case #%d: %I64d\n", ++kase, ans);
    }

    return 0;
}

/*
  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
  1   1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
  2   0   0   1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
  3   0   1   0   0   1   0   0   0   0   0   0   0   0   0   0   0   0   0
  4   0   0   0   2   0   0   1   0   0   0   0   0   0   0   0   0   0   0
  5   0   0   1   0   0   3   0   0   1   0   0   0   0   0   0   0   0   0
  6   0   0   0   0   3   0   0   4   0   0   1   0   0   0   0   0   0   0
  7   0   0   0   1   0   0   6   0   0   5   0   0   1   0   0   0   0   0
  8   0   0   0   0   0   4   0   0  10   0   0   6   0   0   1   0   0   0
  9   0   0   0   0   1   0   0  10   0   0  15   0   0   7   0   0   1   0
 10   0   0   0   0   0   0   5   0   0  20   0   0  21   0   0   8   0   0
 11   0   0   0   0   0   1   0   0  15   0   0  35   0   0  28   0   0   9
 12   0   0   0   0   0   0   0   6   0   0  35   0   0  56   0   0  36   0
 13   0   0   0   0   0   0   1   0   0  21   0   0  70   0   0  84   0   0
 14   0   0   0   0   0   0   0   0   7   0   0  56   0   0 126   0   0 120
 15   0   0   0   0   0   0   0   1   0   0  28   0   0 126   0   0 210   0
 16   0   0   0   0   0   0   0   0   0   8   0   0  84   0   0 252   0   0
 17   0   0   0   0   0   0   0   0   1   0   0  36   0   0 210   0   0 462
 18   0   0   0   0   0   0   0   0   0   0   9   0   0 120   0   0 462   0
*/